package producersConsumersTests.meanRemovalTime;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import prod_cons.TaskContainer;
import producersConsumersTests.utils.DummyTask;
import producersConsumersTests.utils.DummyTaskFactory;
import producersConsumersTests.utils.Inserter;
import producersConsumersTests.utils.Pair;
import producersConsumersTests.utils.Remover;
import producersConsumersTests.utils.Test;
import producersConsumersTests.utils.Tester;
import producersConsumersTests.utils.TesterVisitor;

public class MeanRemovalTimeTest implements Test {
	private static class MeanMeasureRemover extends Remover {
		private long start;
		private Collection<Pair<Double, Integer>> measures = new LinkedList<Pair<Double, Integer>>();
		private final int removesBetweenMeasures;
		private int removesNum = 0, removedNum = 0;

		public MeanMeasureRemover(TaskContainer container,
				int removesBetweenMeasures) {
			super(container);
			this.removesBetweenMeasures = removesBetweenMeasures;
		}
		
		@Override
		protected void beforeRemoverRun() {
			this.removesNum = 0;
			start = System.nanoTime();
		}

		@Override
		protected void afterGetAttempt(Runnable task) {
			if (task != null)
				removedNum++;
			if (++removesNum == removesBetweenMeasures) {
				long finish = System.nanoTime();
				double time = (double)(finish - start) / 1000000;
				measures.add(new Pair<Double, Integer>(time, removedNum));
				removesNum = 0;
				removedNum = 0;
				start = finish;
			}
		}

		public Collection<Pair<Double, Integer>> getMeasures() {
			return measures;
		}
	}

	private class MeanRemovalTimeTester extends
			Tester<Inserter<DummyTask>, MeanMeasureRemover> {
		public MeanRemovalTimeTester(Inserter<DummyTask>[] inserters,
				MeanMeasureRemover[] removers, int timeToRun,
				TaskContainer container) {
			super(inserters, removers, timeToRun, container);
		}

		private List<Pair<Double, Integer>> measurements = new LinkedList<Pair<Double, Integer>>();
		private double avg;

		@Override
		protected void afterRun() {
			double sumTimes = 0;
			long sumRemovals = 0;
			for (MeanMeasureRemover remover : removers) {
				measurements.addAll(remover.getMeasures());
				for (Pair<Double, Integer> measure : remover.getMeasures()) {
					sumTimes += measure.first;
					sumRemovals += measure.second;
				}
			}
			avg = sumTimes / sumRemovals;
			System.out.println("Total mean time for all "
					+ (measurements.size()) + " measurements = " + avg + "ms/task");
		}

		public List<Pair<Double, Integer>> getMeasures() {
			return measurements;
		}

		@Override
		protected void beforeRun() {
		}

		public double getAverage() {
			return avg;
		}
	}

	private static class DumbInserter extends Inserter<DummyTask> {
		public DumbInserter(TaskContainer container, double lambda) {
			super(container, lambda, new DummyTaskFactory());
		}

		public DumbInserter(TaskContainer container, int peakLength,
				int timeBetweenPeaks) {
			super(container, peakLength, timeBetweenPeaks,
					new DummyTaskFactory());
		}
	}

	private int removesBetweenInsertions;
	private MeanRemovalTimeTester tester;

	@Override
	public void test(int insertersNum, int removersNum, int peakLength,
			int timeBetweenPeaks, int timeToRun, TaskContainer container,
			int extraInt) throws InterruptedException, InstantiationException,
			IllegalAccessException, ClassNotFoundException {
		removesBetweenInsertions = extraInt;
		DumbInserter inserters[] = new DumbInserter[insertersNum];
		for (int i = 0; i < inserters.length; i++)
			inserters[i] = new DumbInserter(container, peakLength,
					timeBetweenPeaks);
		MeanMeasureRemover removers[] = new MeanMeasureRemover[removersNum];
		for (int i = 0; i < removers.length; i++)
			removers[i] = new MeanMeasureRemover(container,
					removesBetweenInsertions);
		tester = new MeanRemovalTimeTester(inserters, removers, timeToRun,
				container);
		tester.runTest();
	}

	@Override
	public void test(int insertersNum, int removersNum, double lambda,
			int timeToRun, TaskContainer container, int extraInt)
			throws InterruptedException, InstantiationException,
			IllegalAccessException, ClassNotFoundException {
		removesBetweenInsertions = extraInt;
		DumbInserter inserters[] = new DumbInserter[insertersNum];
		for (int i = 0; i < inserters.length; i++)
			inserters[i] = new DumbInserter(container, lambda);
		MeanMeasureRemover removers[] = new MeanMeasureRemover[removersNum];
		for (int i = 0; i < removers.length; i++)
			removers[i] = new MeanMeasureRemover(container,
					removesBetweenInsertions);
		tester = new MeanRemovalTimeTester(inserters, removers, timeToRun,
				container);
		tester.runTest();
	}

	public List<Pair<Double, Integer>> getResults() {
		return tester.getMeasures();
	}

	@Override
	public void accept(TesterVisitor v) {
		v.visit(this);
	}

	public int getRemovesBetweenMeasures() {
		return removesBetweenInsertions;
	}

	public double getAverage() {
		return tester.getAverage();
	}
}
